package com.github.cyf.interceptor.encrypt;

import com.github.cyf.util.MSUtil;
import com.github.cyf.util.PrintUtil;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.lang.reflect.Method;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 通过拦截器对返回结果中的某个字段进行加密处理
 *
 * @author chenyifan
 * @create 2024-04-14 17:55
 */
@Intercepts({
        @Signature(
                type = ResultSetHandler.class,
                method = "handleResultSets",
                args = {Statement.class}
        )
})
public class EncryptResultFieldInterceptor implements Interceptor {

    private static PrintUtil printUtil = new PrintUtil(EncryptResultFieldInterceptor.class);

    private final Map<String, IEncryptResultFieldStrategy> encryptMap;

    public EncryptResultFieldInterceptor(List<IEncryptResultFieldStrategy> encryptStrategies) {
        this.encryptMap = encryptStrategies.stream().collect(Collectors.toMap(ele -> ele.getClass().getSimpleName(), ele -> ele));
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        printUtil.printInterceptorBegin();
        Object returnValue = invocation.proceed();
        ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget();
        // 获取 Mapper 方法上的 EncryptResult 注解
        MetaObject metResultSetHandler = SystemMetaObject.forObject(resultSetHandler);
        MappedStatement ms = (MappedStatement) metResultSetHandler.getValue("mappedStatement");
        Method method = MSUtil.getMapperMethod(ms);
        if (method != null) {
            EncryptResult annotation = method.getAnnotation(EncryptResult.class);
            // 执行被代理方法，获取返回结果，对结果进行加密
            if (annotation != null && returnValue != null) {
                Class<? extends IEncryptResultFieldStrategy> strategy = annotation.encryptStrategy();
                encryptFields(returnValue, annotation.fields(), encryptMap.get(strategy.getSimpleName()));
            }
        }
        printUtil.printInterceptorEnd();
        return returnValue;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * 加密对象的属性
     *
     * @param value    对象
     * @param fields   属性名称数组
     * @param strategy 加密策略
     */
    private void encryptFields(Object value, String[] fields, IEncryptResultFieldStrategy strategy) throws IllegalAccessException {
        if (value == null || strategy == null) {
            return;
        }
        if (value instanceof Iterable) {
            for (Object item : (Iterable<?>) value) {
                encryptFields(item, fields, strategy);
            }
        } else {
            for (String field : fields) {
                Object fieldValue = FieldUtils.readField(value, field);
                if (fieldValue instanceof String) {
                    FieldUtils.writeField(value, field, strategy.encrypt((String) fieldValue));
                }
            }
        }
    }

}